package com.efivestar.idm.api.service.lock.protocol.handler;

import com.efivestar.idm.api.common.Cons;
import com.efivestar.idm.api.domain.*;
import com.efivestar.idm.api.exception.ErrorParamException;
import com.efivestar.idm.api.repository.CameraMapper;
import com.efivestar.idm.api.repository.EventMapper;
import com.efivestar.idm.api.repository.LockRemoteControlMapper;
import com.efivestar.idm.api.repository.WeixinJoinDeviceMapper;
import com.efivestar.idm.api.service.DeviceService;
import com.efivestar.idm.api.service.WeixinService;
import com.efivestar.idm.api.service.dto.PowerDto;
import com.efivestar.idm.api.util.ByteUtils;
import com.efivestar.idm.api.util.weixin.WeixinApi;
import com.efivestar.idm.api.util.weixin.bean.TemplateMessage;
import io.netty.buffer.ByteBuf;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.time.DateFormatUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import tk.mybatis.mapper.entity.Example;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 开门.
 */
@Service
public class OpenDoorLockProtocolHandler extends AbstractLockProtocolHandler {

    Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private EventMapper mapper;


    @Autowired
    private DeviceService deviceService;

    @Autowired
    private WeixinService weixinService;


    @Autowired
    private WeixinJoinDeviceMapper weixinJoinDeviceMapper;

    @Autowired
    private CameraMapper cameraMapper;

    @Autowired
    private LockRemoteControlMapper lockRemoteControlMapper;

    @Autowired
    private WeixinApi weixinApi;

    @Value("${weixin.warningTemplateId:jZt8KiYA6wsIkVqNqL12GAfVnzZftFD895bEva0zPLA}")
    private String warningWeixinTemplateId;

    @Value("${service.domain:51ai.cc}")
    private String domain;


    @Override
    protected int getDataLenthWithoutDeviceId() {
        return 9;
    }

    /**
     * @param buf
     * @param lockId
     * @return
     */
    @Override
    public byte[] handleData(ByteBuf buf, String lockId) {
        int length = buf.readableBytes();
        //跳过指令码，不读取
        buf.skipBytes(1);
        // 读取电量字节
        byte powerBt = buf.readByte();
        PowerDto power = ByteUtils.getPower(powerBt);
        // 读取开门类型字节
        byte eventType = buf.readByte();
        // 读取编码
        ByteBuf codeBf = buf.readBytes(2);
        String userCode = ByteUtils.getUserCode(codeBf);
        // 读取序列号
        long sequence = -1;
        int seqLength = 4;
        int seqIndex = getDataLenthWithoutDeviceId() - seqLength;
        if (seqIndex > 0) {
            byte seqByte[] = new byte[seqLength];
            buf.getBytes(seqIndex, seqByte, 0, seqLength);
            sequence = Long.parseLong(ByteUtils.bytes2hex(seqByte), 16);
        }
        //设置设备的电量和更新时间
        deviceService.updatePower(lockId, power.getPowerPercent());
        int eType = getEventType(eventType);
        EventDomain event = EventDomain.builder().deviceId(lockId).eventType(Cons.OPEN_DOOR_EVENT_TYPE).subEventType(eType).content(userCode).createTime(new Date()).build();
        mapper.insert(event);
        // 检查是否是远程开锁失败了
        boolean verifyFlag = (eType == Cons.OPEN_DOOR_BY_CLOUD_FAIL_EVENT && isSameSequence(sequence, lockId));
        if (verifyFlag) {// 更新此次开锁请求为未操作，以便用户可以再次输入密码开锁。
            opLockRemoteControl(sequence, lockId, true);
        }
        //获取设备的所有用户.
        List<Lock2UserDomain> lock2Users = weixinService.queryLockUsersByLockId(lockId);
        //当前用户在用户列表中
        Lock2UserDomain lockUser = getLockUser(lock2Users, eventType, userCode);
        if (lockUser != null) {
            // 用户是劫持用户
            if (lockUser.isHijackStatus()) {
                sendNotification(lockId, true, eType, lockUser, verifyFlag);
            } else {
                sendNotification(lockId, false, eType, lockUser, verifyFlag);
            }
        } else {
            // 当前用户不在用户列表，所以只能发送用户code
            sendNotification(lockId, eType, userCode, verifyFlag);
            logger.error("用户不存在,开门时设备id:{},用户code:{}, 事件类型:{}, ", new Object[]{lockId, userCode, eventType});
        }

        return ByteUtils.hexToBytes("00");
    }

    public boolean isSameSequence(long sequence, String lockId) {
        boolean verifyEndPoint = (null != deviceService.getDeviceById(lockId) && deviceService.getDeviceById(lockId).getVerifyEndPoint() == 1);
        if (sequence >= 0 && sequence < 4294967295l) {
            LockRemoteControlDomain myLockRemoteControl = lockRemoteControlMapper.selectByPrimaryKey(lockId);
            return verifyEndPoint && myLockRemoteControl != null && myLockRemoteControl.getSequence() == sequence;
        }
        return false;
    }

    public void opLockRemoteControl(long sequence, String lockId, boolean isAllowed) {
        LockRemoteControlDomain myLockRemoteControl = lockRemoteControlMapper.selectByPrimaryKey(lockId);
        if (myLockRemoteControl != null && myLockRemoteControl.getSequence() == sequence) {
            myLockRemoteControl.setOperated(false);
            myLockRemoteControl.setAllowed(isAllowed);
            myLockRemoteControl.setRequestTime(new Date());
            lockRemoteControlMapper.updateByExample(myLockRemoteControl, updateRemoteLockOpStatusWhereCause(lockId));
        }
    }

    private Example updateRemoteLockOpStatusWhereCause(String lockId) {
        Example example = new Example(LockRemoteControlDomain.class);
        Example.Criteria criteria = example.createCriteria();
        criteria.andEqualTo("lockId", lockId);
        criteria.andEqualTo("operated", true);
        return example;
    }


    /**
     * 发送通知信息.
     * 如果是劫持，则信息内容为红色， 并且可以查看摄像头.否则就是普通的信息，点击之后可以跳转到事件列表界面.
     *
     * @param deviceId
     * @param isHijack
     */
    private void sendNotification(String deviceId, boolean isHijack, int eventType, Lock2UserDomain hijack, boolean remotePwdErr) {
        //发送微信通知.
        String now = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss");
        List<WeixinJoinDeviceDomain> weixinJoinDeviceDomains = weixinJoinDeviceMapper.selectByExample(generateUserJoinDeviceExample(deviceId));
        for (WeixinJoinDeviceDomain weixinJoinDeviceDomain : weixinJoinDeviceDomains) {
            String toUser = weixinJoinDeviceDomain.getOpenId();
            Map map = new HashMap<>();
            if (isHijack) {
                map.put("first", TemplateMessage.builder().value("开门通知").color("#74F192").build());
                map.put("keyword1", TemplateMessage.builder().value("用户 " + (StringUtils.isEmpty(hijack.getUserName()) ? hijack.getUserCode() : hijack.getUserName()) + " 遭到劫持").color("#F00").build());
                map.put("keyword2", TemplateMessage.builder().value(now).color("#F00").build());
                map.put("remark", TemplateMessage.builder().value("点击查看摄像头").build());
                String cameraUrl = "http://" + domain + "/distIndex#/main/realtime_camera";
                if (StringUtils.isNotEmpty(weixinJoinDeviceDomain.getCameraId())) {
                    CameraDomain cameraDomain = CameraDomain.builder().cameraId(weixinJoinDeviceDomain.getCameraId()).build();
                    CameraDomain dbCamera = cameraMapper.selectByPrimaryKey(cameraDomain);
                    if (dbCamera != null && StringUtils.isNotEmpty(dbCamera.getUrl())) {
//                        cameraUrl = "http://" + domain + "/cameraPage";// 老版本地址
                        cameraUrl = "http://" + domain + "/distIndex#/main/realtime_camera";// 新版地址
                        map.put("remark", TemplateMessage.builder().value("点击查看摄像头").color("#F00").build());
                    } else {
                        logger.error("摄像头:{},不存在对应的视频连接。但是关系表中存在关联关系", weixinJoinDeviceDomain.getCameraId());
                    }
                }
                boolean result = weixinApi.sendTemplateMessage(toUser, warningWeixinTemplateId, cameraUrl,
                        map, "#F00");
                logger.info("开门推送，智能锁id:{}, toUser:{}, 结果: {}", new Object[]{deviceId, toUser, result});
            } else {
                String eventDetail = generateSendMsg(deviceId, eventType, (StringUtils.isEmpty(hijack.getUserName()) ? hijack.getUserCode() : hijack.getUserName()), map, remotePwdErr);
                boolean result = weixinApi.sendTemplateMessage(toUser, warningWeixinTemplateId, eventDetail,
                        map, null);
                logger.info("开门推送，智能锁id:{}, toUser:{}, 结果： {}", new Object[]{deviceId, toUser, result});
            }
        }
    }


    /**
     * 发送通知信息.
     * 如果是劫持，则信息内容为红色， 并且可以查看摄像头.否则就是普通的信息，点击之后可以跳转到事件列表界面.
     *
     * @param deviceId
     */
    private void sendNotification(String deviceId, int eventType, String userCode, boolean remotePwdErr) {
        //发送微信通知.
        List<WeixinJoinDeviceDomain> weixinJoinDeviceDomains = weixinJoinDeviceMapper.selectByExample(generateUserJoinDeviceExample(deviceId));
        for (WeixinJoinDeviceDomain weixinJoinDeviceDomain : weixinJoinDeviceDomains) {
            String toUser = weixinJoinDeviceDomain.getOpenId();
            Map map = new HashMap<>();
            String eventDetail = generateSendMsg(deviceId, eventType, userCode, map, remotePwdErr);
            boolean result = weixinApi.sendTemplateMessage(toUser, warningWeixinTemplateId, eventDetail,
                    map, null);
            logger.info("开门推送，智能锁id:{}, toUser:{}, 结果： {}", new Object[]{deviceId, toUser, result});
        }
    }

    private String generateSendMsg(String deviceId, int eventType, String userCode, Map map, boolean remotePwdErr) {
        String now = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss");
        map.put("first", TemplateMessage.builder().value("开门通知").color("#74F192").build());
        String noticeTitle = "";
        String eventDetail = "http://" + domain + "/distIndex#/main/jiankong";
        if (eventType == Cons.OPEN_DOOR_BY_CLOUD_SUCCESS_EVENT) {
            noticeTitle = getEventTypeName(eventType);
        } else if (eventType == Cons.OPEN_DOOR_BY_CLOUD_FAIL_EVENT) {
            if (remotePwdErr) {
                noticeTitle = getEventTypeName(eventType);
                eventDetail = "http://" + domain + "/distIndex#/main/kaisuo";
            } else {
                noticeTitle = "远程开门失败";
            }
        } else {
            noticeTitle = "用户 " + userCode + "(" + getEventTypeName(eventType) + ") 回家了";
        }
        map.put("keyword1", TemplateMessage.builder().value(noticeTitle).build());
        map.put("keyword2", TemplateMessage.builder().value(now).build());
        map.put("remark", TemplateMessage.builder().value("点击查看摄像头").build());

        if (eventType == Cons.OPEN_DOOR_BY_KEY_EVENT) {//（钥匙）强行开门在布防状态下报警
            DeviceDomain deviceDomain = deviceService.getDeviceById(deviceId);
            if (deviceDomain != null && deviceDomain.getDefenseStatus() == 1) {
                map.put("first", TemplateMessage.builder().value("您的锁有新的报警信息").color("#F00").build());
                map.put("keyword1", TemplateMessage.builder().value("布防状态下尝试钥匙开锁").color("#F00").build());
                map.put("keyword2", TemplateMessage.builder().value(now).color("#F00").build());
                map.put("remark", TemplateMessage.builder().value("点击查看摄像头").color("#F00").build());
                // eventDetail = "http://" + domain + "/cameraPage";// 老版本地址
                eventDetail = "http://" + domain + "/distIndex#/main/realtime_camera";// 新版地址
            }
        }

        return "http://" + domain + "/distIndex#/main/realtime_camera";
    }


    private String getEventTypeName(int eventType) {
        switch (eventType) {
            case Cons.OPEN_DOOR_BY_FIGURE_EVENT:
                return "指纹";
            case Cons.OPEN_DOOR_BY_PASSWORD_EVENT:
                return "密码";
            case Cons.OPEN_DOOR_BY_CARD_EVENT:
                return "卡";
            case Cons.OPEN_DOOR_BY_KEY_EVENT:
                return "钥匙";
            case Cons.OPEN_DOOR_BY_REMOTE_CONTROL_EVENT:
                return "遥控";
            case Cons.OPEN_DOOR_BY_CLOUD_SUCCESS_EVENT:
                return "远程开门成功";
            case Cons.OPEN_DOOR_BY_CLOUD_FAIL_EVENT:
                return "远程开门密码不匹配";
        }
        return "";
    }

    private Example generateUserJoinDeviceExample(String deviceId) {
        Example example = new Example(WeixinJoinDeviceDomain.class);
        Example.Criteria criteria = example.createCriteria();
        if (StringUtils.isNotEmpty(deviceId)) {
            criteria.andEqualTo("deviceId", deviceId);
        }
        return example;
    }


    private Lock2UserDomain getLockUser(List<Lock2UserDomain> lock2Users, int type, String userCode) {
        if (lock2Users != null) {
            List<Lock2UserDomain> hiJackUsers = lock2Users.stream().filter(d -> (d.getType().intValue() == type && d.getUserCode().equalsIgnoreCase(userCode))).collect(Collectors.toList());
            return hiJackUsers.size() > 0 ? hiJackUsers.get(0) : null;
        }
        return null;
    }


    /**
     * 获取开门的子事件类型.
     *
     * @param eventTypeBt
     * @return
     */
    private int getEventType(byte eventTypeBt) {
        String eventType = ByteUtils.bytes2hex(eventTypeBt);
        switch (eventType) {
            case "00":
                return Cons.OPEN_DOOR_BY_FIGURE_EVENT;
            case "01":
                return Cons.OPEN_DOOR_BY_PASSWORD_EVENT;
            case "02":
                return Cons.OPEN_DOOR_BY_CARD_EVENT;
            case "03":
                return Cons.OPEN_DOOR_BY_KEY_EVENT;
            case "04":
                return Cons.OPEN_DOOR_BY_REMOTE_CONTROL_EVENT;
            case "05":
                return Cons.OPEN_DOOR_BY_CLOUD_SUCCESS_EVENT;
            case "06":
                return Cons.OPEN_DOOR_BY_CLOUD_FAIL_EVENT;
        }
        throw new ErrorParamException();
    }


    public static byte[] subBytes(byte[] src, int begin, int count) {
        byte[] bs = new byte[count];
        System.arraycopy(src, begin, bs, 0, count);
        return bs;
    }

    @Override
    public String udpHead() {
        return Cons.UDP_HEAD_EF01;
    }

    @Override
    public String udpCommand() {
        return Cons.UDP_COMMAND_OPEN_DOOR;
    }

    public static void main(String[] args) {
        System.out.println(Integer.parseUnsignedInt("c3", 16));
    }

}






